\chapter{C Client API}
\label{chap:api:c-client}

The HyperDex Client library, \code{libhyperdex-client} is the de facto way to
access a HyperDex cluster for storing and retrieving data.  All data-store
operations are provided by the this library.

\section{Building the HyperDex C Binding}
\label{sec:api:c:client:build}

The HyperDex C Binding is automatically built and installed via the normal
HyperDex build and install process.  You can ensure that the client is always
built by providing the \code{--enable-client} option to \code{./configure} like
so:

\begin{consolecode}
% ./configure --enable-client
\end{consolecode}

\section{Compiling and Linking Your Application}
\label{sec:api:c:client:link}
Unless otherwise noted, all Client operations are defined in the
\code{hyperdex/client.h} include.  You can include this in your own program
with:

\begin{ccode}
#include <hyperdex/client.h>
\end{ccode}

To link against \code{libhyperdex-client}, provide the \code{-lhyperdex-client}
option at link time:

\begin{consolecode}
% cc -o output input.c -I/path/to/hyperdex/include -L/path/to/hyperdex/lib -lhyperdex-client
\end{consolecode}

HyperDex provides support for the automatically determining the compiler and
linker flags for \code{libhyperdex-client}.  First, ensure the \code{pkg-config}
program is installed, and then run:

\begin{consolecode}
% pkg-config --cflags hyperdex-client -I/usr/local/include
% pkg-config --libs hyperdex-client -L/usr/local/lib -lhyperdex-client
\end{consolecode}

The first command outputs the compiler flags necessary to include the
\code{hyperdex/client.h} file, while the second command outputs the flags
necessary for linking against \code{libhyperdex-client}.

To put it all together, you can compile your application with:

\begin{consolecode}
% cc -o output input.c `pkg-config --cflags --libs hyperdex-client`
\end{consolecode}

For more information about \code{pkg-config}, see the
\href{http://people.freedesktop.org/~dbn/pkg-config-guide.html#using}{pkg-config
documentation}.

\section{Hello World}
\label{sec:api:c:client:helloworld}

The following is a minimal application that stores the value "Hello World" and
then immediately retrieves the value:

\inputminted{c}{\topdir/c/client/hello-world.c}

You can compile and run this example with:

\begin{consolecode}
% cc -o hello-world hello-world.c `pkg-config --cflags --libs hyperdex-client`
% ./hello-world
put "Hello World!"
get done
got attribute "v" = "Hello World!"
\end{consolecode}

Each operation, whether it is a PUT or a GET, is broken down into a request and
a response.  The \code{hyperdex\_client\_put} and \code{hyperdex\_client\_get}
operations each initiate the request.  The application then calls
\code{hyperdex\_client\_loop} to wait for the completion of the operation.  The
status pointer passed to put or get will be used to fill in the status of the
operation, while the status pointer passed to loop will be used to report
transient problems with the cluster.  Until the operation's id is returned from
loop, the operation is still outstanding and incomplete.

The stored value is passed via \code{struct hyperdex\_client\_attribute}, which
specifies both the value of the string "Hello World!" and that this value is, in
fact, a string.  All HyperDex datatypes are passed to HyperDex as bytestrings
via this interface.

This code omits error checking, assuming that all operations succeed.  In the
subsequent documentation we'll explore proper error checking, which should be in
place for all non-example code.

\section{Asynchronous Patterns}
\label{sec:api:c:client:async}

All operations are issued {\em asynchronously}, that is, the operation's start
is decoupled from its completion, and it is up to the application to wait for
its completion.

The C API provides a unified, event-loop-like interface for polling events for
their completion.  An application can issue any number of operations, and poll
for their completion using the \code{hyperdex\_client\_loop} call.  This
provides you with several advantages unavailable in any other distributed
key-value store:

\begin{itemize}
\item Asynchronous events free your application to perform other operations
while waiting for events to complete.

\item Asynchronous events re-use underlying resources, such as TCP connections
to the cluster, across operations.  This enables improved performance and
consumes fewer resources than would be consumed by synchronous clients
generating a similar workload.

\item Operations will be buffered in userspace when they cannot be immediately
sent to a server.  This ensures that applications will never block waiting for
slow servers.
\end{itemize}

Of course, applications do not need to embrace this asynchronous design pattern.
It's always possible to use the library in a synchronous manner by immediately
following every operation with a call to \code{hyperdex\_client\_loop}.

Applications that do embrace this asynchronous design pattern will have a
certain structure.  Specifically:

\begin{itemize}
\item Each operation must eventually be followed by a call to
\code{hyperdex\_client\_loop}.  This is the core of the HyperDex client.  All
work, including flushing userspace buffers, occurs via the call to loop.

\item Pointers used for output from operations must remain valid until
\code{hyperdex\_client\_loop} indicates that the operation has successfully
finished.  Consequently, they must not be aliased to pointers passed to other
operations.
\end{itemize}

Finally, it's important to realize that calling \code{hyperdex\_client\_loop} is
necessary to complete operations.  An operation's outcome is not determined
until the application calls \code{hyperdex\_client\_loop}.  Do not simply issue
\code{hyperdex\_client\_put} operations (or similar) and assume that the
operations complete because there is no guarantee that they will do so.

\section{Creating a Client}
\label{sec:api:c:client:create}

A HyperDex client is encapsulated within the incomplete \code{struct
hyperdex\_client} type.  This type is created by the HyperDex client library,
and should only be freed using the provided method.

\begin{ccode}
struct hyperdex_client*
hyperdex_client_create(const char* coordinator, uint16_t port);
\end{ccode}
Create a new client instance.  This call allocates and initializes
local structures.  If allocation or initialization fail, the call will return
NULL and set \code{errno} appropriately.  This call does not establish the
connection to the coordinator; that will be established and maintained
automatically by other calls made with this client instance.

\textbf{Parameters:}
\begin{description}[labelindent=\widthof{{\code{coordinator}}},leftmargin=*,noitemsep,nolistsep,align=right]
\item[\code{coordinator}] A C-string containing IP address or hostname of the
    coordinator.
\item[\code{port}] The port number for the coordinator.
\end{description}

\begin{ccode}
void
hyperdex_client_destroy(struct hyperdex_client* client);
\end{ccode}
Destroy a previously instantiated client, and release all associated
resources.  This call always succeeds.

\textbf{Parameters:}
\begin{description}[labelindent=\widthof{{\code{client}}},leftmargin=*,noitemsep,nolistsep,align=right]
\item[\code{client}] A previously created client instance.
\end{description}

\section{Data Structures}
\label{sec:api:c:client:data-structures}

HyperDex natively supports a variety of data structures.  This section describes
the available data structures and their encoding within C.  HyperDex encodes as
a byte string all data structures passed between the application and HyperDex.
This format of this byte string varies according to its type.  In this section,
we'll describe the format of data structures, and provide an API for serializing
to and from the prescribed format.  All APIs discussed in this section are
provided by \code{libhyperdex-client}.

\subsection{\code{enum hyperdatatype}}
\label{sec:api:c:client:hyperdatatype}

The \code{enum hyperdatatype} is used to represent the type of a byte string to
HyperDex.  Whenever a structure accepts a byte string as a value, it will
typically accept an \code{enum hyperdatatype} to convey the type of the string.

\paragraph{Primitive Data Types}

Primitive data types are the basic data types of HyperDex.  Applications may use
these primitives as the key and dimensions of hyperspaces within HyperDex.

\begin{description}[noitemsep]
\item[\code{HYPERDATATYPE\_STRING}] A byte string.
\item[\code{HYPERDATATYPE\_INT64}] A 64-bit signed integer.
\item[\code{HYPERDATATYPE\_FLOAT}] A 64-bit floating point value.
\end{description}

\paragraph{Container Data Types}

Container data types contain a collection of primitive data types.  Container
data types cannot be used as the key or dimensions of the hyperspace.

There are three container types available within HyperDex:

\begin{description}
\item[Lists] A list contains elements of one primitive type.  The order of
    elements in a list is preserved, and it's possible for duplicate elements to
    exist.
\item[Sets] A set contains elements of one primitive type.  Each element exists
    in the set at most once.  Although the implementation enforces an order on
    the set for efficiency purposes, set operations are agnostic to the order of
    the included elements.
\item[Maps] A map contains key-value pairs of elements, where the key and value
    may be of different types.  Each key is unique and has an associated value.
    Maps also offer the ability to perform most primitive operations on the
    values within the map.
\end{description}

Each of these containers may be instantiated with a primitive data type as the
contained elements.  In total, HyperDex supports all of the following container
data types:

\begin{description}[noitemsep]
\item[\code{HYPERDATATYPE\_LIST\_STRING}] A list of strings.
\item[\code{HYPERDATATYPE\_LIST\_INT64}] A list of integers.
\item[\code{HYPERDATATYPE\_LIST\_FLOAT}] A list of floats.
\item[\code{HYPERDATATYPE\_SET\_STRING}] A set of strings.
\item[\code{HYPERDATATYPE\_SET\_INT64}] A set of integers.
\item[\code{HYPERDATATYPE\_SET\_FLOAT}] A set of floats.
\item[\code{HYPERDATATYPE\_MAP\_STRING\_STRING}] A map from strings to strings.
\item[\code{HYPERDATATYPE\_MAP\_STRING\_INT64}] A map from strings to integers.
\item[\code{HYPERDATATYPE\_MAP\_STRING\_FLOAT}] A map from strings to floats.
\item[\code{HYPERDATATYPE\_MAP\_INT64\_STRING}] A map from integers to strings.
\item[\code{HYPERDATATYPE\_MAP\_INT64\_INT64}] A map from integers to integers.
\item[\code{HYPERDATATYPE\_MAP\_INT64\_FLOAT}] A map from integers to floats.
\item[\code{HYPERDATATYPE\_MAP\_FLOAT\_STRING}] A map from floats to strings.
\item[\code{HYPERDATATYPE\_MAP\_FLOAT\_INT64}] A map from floats to integers.
\item[\code{HYPERDATATYPE\_MAP\_FLOAT\_FLOAT}] A map from floats to floats.
\end{description}

The following data types are defined as well, and are generally only of interest
to HyperDex developers and those who are writing client bindings:

\begin{description}[noitemsep]
\item[\code{HYPERDATATYPE\_LIST\_GENERIC}] A list whose element type is
    unspecified.
\item[\code{HYPERDATATYPE\_SET\_GENERIC}] A set whose element type is
    unspecified.
\item[\code{HYPERDATATYPE\_MAP\_GENERIC}] A map whose key/value types are
    unspecified.
\item[\code{HYPERDATATYPE\_MAP\_STRING\_KEYONLY}] A map whose key is a string
    and whose value is unspecified.
\item[\code{HYPERDATATYPE\_MAP\_INT64\_KEYONLY}] A map whose key is an integer
    and whose value type is unspecified.
\item[\code{HYPERDATATYPE\_MAP\_FLOAT\_KEYONLY}] A map whose key is a float and
    whose value type is unspecified.
\item[\code{HYPERDATATYPE\_GARBAGE}] A reserved constant never used within
    HyperDex.
\end{description}

\subsection{Bytestring Format}
\label{sec:api:c:client:format}

The format of the data structures is defined to be the same on all platforms.

For each format, Python-like psuedocode is provided that shows example
encodings.

\paragraph{string format}

A string is an 8-bit byte string.  HyperDex is agnostic to the contents of the
string, and it may contain any bytes, including \code{\\x00}.  By convention,
the trailing \code{NULL} should be omitted for C-strings to ensure
interoperability across languages.  For example:

\begin{pythoncode}
>>> encode_string('Hello\x00World!')
b'Hello\x00World!'
\end{pythoncode}

\paragraph{int format}

Integers are encoded as signed 8-byte little-endian integers.  For example:

\begin{pythoncode}
>>> encode_int(1)
b'\x01\x00\x00\x00\x00\x00\x00\x00'
>>> encode_int(-1)
b'\xff\xff\xff\xff\xff\xff\xff\xff'
>>> encode_int(0xdeadbeef)
b'\xef\xbe\xad\xde\x00\x00\x00\x00'
\end{pythoncode}

\paragraph{float format}

Floats are encoded as IEEE 754 binary64 values in little-endian format.  For
example:

\begin{pythoncode}
>>> encode_double(0)
b'\x00\x00\x00\x00\x00\x00\x00\x00'
>>> encode_double(3.1415)
b'o\x12\x83\xc0\xca!\t@'
\end{pythoncode}

\paragraph{list(string) format}

Lists of strings are encoded by concatenating the encoding of each string,
prefixed by an unsigned 4-byte little endian integer indicating the length of
the string.  For example:

\begin{pythoncode}
>>> encode_list_string([])
b''
>>> encode_list_string(['hello', 'world'])
b'\x05\x00\x00\x00hello\x05\x00\x00\x00world'
\end{pythoncode}

\paragraph{list(int) format}

Lists of integers are encoded by concatenating the encoded form of each integer.
For example:

\begin{pythoncode}
>>> encode_list_int([])
b''
>>> encode_list_int([1, -1, 0xdeadbeef])
b'\x01\x00\x00\x00\x00\x00\x00\x00' \
b'\xff\xff\xff\xff\xff\xff\xff\xff' \
b'\xef\xbe\xad\xde\x00\x00\x00\x00'
\end{pythoncode}

\paragraph{list(floats) format}

Lists of floats are encoded by concatenating the encoded form of each float.
For example:

\begin{pythoncode}
>>> encode_list_float([])
b''
>>> encode_list_float([0, 3.1415])
b'\x00\x00\x00\x00\x00\x00\x00\x00' \
b'o\x12\x83\xc0\xca!\t@'
\end{pythoncode}

\paragraph{set(string) format}

Sets of strings are encoded by concatenating the encoding of each string in
sorted order, where each string is prefixed by an unsigned 4-byte little endian
integer indicating the length of the string.  For example:

\begin{pythoncode}
>>> encode_set_string([])
b''
>>> encode_set_string(['world', 'hello'])
b'\x05\x00\x00\x00hello\x05\x00\x00\x00world'
\end{pythoncode}

\paragraph{set(int) format}

Sets of integers are encoded by concatenating the encoded form of each integer
in sorted order.  For example:

\begin{pythoncode}
>>> encode_set_int([])
b''
>>> encode_set_int([1, -1, 0xdeadbeef])
b'\xff\xff\xff\xff\xff\xff\xff\xff' \
b'\x01\x00\x00\x00\x00\x00\x00\x00' \
b'\xef\xbe\xad\xde\x00\x00\x00\x00'
\end{pythoncode}

\paragraph{set(float) format}

Sets of floats are encoded by concatenating the encoded form of each float in
sorted order.  For example:

\begin{pythoncode}
>>> encode_set_float([])
b''
>>> encode_set_float([3.1415, 0])
b'\x00\x00\x00\x00\x00\x00\x00\x00' \
b'o\x12\x83\xc0\xca!\t@'
\end{pythoncode}

\paragraph{map(string, string) format}

Maps from strings to strings are formed by encoding the individual elements,
each prefixed by an unsigned 4-byte little endian integer indicating their
length.  The pairs of elements are stored in sorted order according to the first
element of the pair (the map's key).  For example:

\begin{pythoncode}
>>> encode_map_string_string({})
b''
>>> encode_map_string_string({'hello': 'world',
...                           'map key': 'map val',
...                           'map', 'encoding'})
b'\x05\x00\x00\x00hello\x05\x00\x00\x00world' \
b'\x03\x00\x00\x00map\x08\x00\x00\x00encoding' \
b'\x07\x00\x00\x00map key\x07\x00\x00\x00map val'
\end{pythoncode}

\paragraph{map(string, int) format}

Maps from strings to ints are formed by encoding the individual elements, where
keys are prefixed by an unsigned 4-byte little endian integer indicating their
length.  The pairs of elements are stored in sorted order according to the first
element of the pair (the map's key).  For example:

\begin{pythoncode}
>>> encode_map_string_int({})
b''
>>> encode_map_string_int({'world': -1,
...                        'hello': 1})
b'\x05\x00\x00\x00hello\x01\x00\x00\x00\x00\x00\x00\x00' \
b'\x05\x00\x00\x00world\xff\xff\xff\xff\xff\xff\xff\xff'
\end{pythoncode}

\paragraph{map(string, float) format}

Maps from strings to floats are formed by encoding the individual elements,
where keys are prefixed by an unsigned 4-byte little endian integer indicating
their length.  The pairs of elements are stored in sorted order according to the
first element of the pair (the map's key).  For example:

\begin{pythoncode}
>>> encode_map_string_float({})
b''
>>> encode_map_string_float({'zero': 0,
...                          'pi': 3.1415})
b'\x02\x00\x00\x00pio\x12\x83\xc0\xca!\t@' \
b'\x04\x00\x00\x00zero\x00\x00\x00\x00\x00\x00\x00\x00'
\end{pythoncode}

\paragraph{map(int, string) format}

Maps from ints to strings are formed by encoding the individual elements, where
values are prefixed by an unsigned 4-byte little endian integer indicating their
length.  The pairs of elements are stored in sorted order according to the first
element of the pair (the map's key).  For example:

\begin{pythoncode}
>>> encode_map_int_string({})
b''
>>> encode_map_int_string({1: 'hello',
...                        -1: 'world'})
b'\xff\xff\xff\xff\xff\xff\xff\xff\x05\x00\x00\x00world' \
b'\x01\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00hello'
\end{pythoncode}

\paragraph{map(int, int) format}

Maps from ints to ints are formed by encoding the individual elements.  The
pairs of elements are stored in sorted order according to the first element of
the pair (the map's key).  For example:

\begin{pythoncode}
>>> encode_map_int_int({})
b''
>>> encode_map_int_int({1: 0xdeadbeef,
...                     -1: 0x1eaff00d})
b'\xff\xff\xff\xff\xff\xff\xff\xff\x0d\xf0\xaf\x1e\x00\x00\x00\x00' \
b'\x01\x00\x00\x00\x00\x00\x00\x00\xef\xbe\xad\xde\x00\x00\x00\x00'
\end{pythoncode}

\paragraph{map(int, float) format}

Maps from ints to floats are formed by encoding the individual elements.  The
pairs of elements are stored in sorted order according to the first element of
the pair (the map's key).  For example:

\begin{pythoncode}
>>> encode_map_int_float({})
b''
>>> encode_map_int_float({1: 0,
...                       -1: 3.1415})
b'\xff\xff\xff\xff\xff\xff\xff\xffo\x12\x83\xc0\xca!\t@' \
b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
\end{pythoncode}

\paragraph{map(float, string) format}

Maps from floats to strings are formed by encoding the individual elements,
where values are prefixed by an unsigned 4-byte little endian integer indicating
their length.  The pairs of elements are stored in sorted order according to the
first element of the pair (the map's key).  For example:

\begin{pythoncode}
>>> encode_map_float_string({})
b''
>>> encode_map_float_string({0: 'hello',
...                          3.1415: 'world'})
b'\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00hello' \
b'o\x12\x83\xc0\xca!\t@\x05\x00\x00\x00world'
\end{pythoncode}

\paragraph{map(float, int) format}

Maps from floats to ints are formed by encoding the individual elements.  The
pairs of elements are stored in sorted order according to the first element of
the pair (the map's key).  For example:

\begin{pythoncode}
>>> encode_map_float_int({})
b''
>>> encode_map_float_int({0: 1,
...                       3.1415: -1})
b'\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00' \
b'o\x12\x83\xc0\xca!\t@\xff\xff\xff\xff\xff\xff\xff\xff'
\end{pythoncode}

\paragraph{map(float, float) format}

Maps from floats to floats are formed by encoding the individual elements.  The
pairs of elements are stored in sorted order according to the first element of
the pair (the map's key).  For example:

\begin{pythoncode}
>>> encode_map_float_float({})
b''
>>> encode_map_float_float({0: 1,
...                         3.1415: -1})
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?' \
b'o\x12\x83\xc0\xca!\t@\x00\x00\x00\x00\x00\x00\xf0\xbf'
\end{pythoncode}

\subsection{Serialization API}
\label{sec:api:c:client:serialize}

The serialization API supports serialization of all datatypes supported by
HyperDex.  Of course, feel free to manually encode data structures, especially
where doing so can make use of efficient stack-allocated data structures.

\paragraph{struct hyperdex\_ds\_arena}

The packing routines described below may occasionally have to allocate memory
into which the encoded forms of the datatypes are copied.  To free the
programmer from the burden of having to manually allocate and free each of these
pieces of memory, the data structures API allocates all memory via an instance
of \code{struct hyperdex\_ds\_arena}.  Via a single call to
\code{hyperdex\_ds\_arena\_destroy}, all memory allocated via the arena is
free'd.

\code{struct hyperdex\_ds\_arena} is intentionally defined as an incomplete type
because its internals are subject to change.  To create an arena, call
\code{hyperdex\_ds\_arena\_create}.  The arena should subsequently be destroyed
via \code{hyperdex\_ds\_arena\_destroy}.

\begin{ccode}
struct hyperdex_ds_arena* hyperdex_ds_arena_create();
\end{ccode}
Create a new arena for alocating memory.  On success, this function
returns a non-null pointer for the new arena.  On failure, the function returns
\code{NULL}, indicating that memory allocation failed.  It is the caller's
responsibility to pass this function to \code{hyperdex\_ds\_arena\_destroy} when
finished.

\begin{ccode}
void hyperdex_ds_arena_destroy(struct hyperdex_ds_arena* arena);
\end{ccode}
Free all memory associated with \code{arena}.  This function always
succeeds.

\paragraph{serialize string}

No serialization is necessary for string data types.  For convenience, the
serialization API provides a copy function that copies a string into
arena-allocated memory.

\begin{ccode}
int hyperdex_ds_copy_string(struct hyperdex_ds_arena* arena, const char* str,
                            size_t str_sz, enum hyperdex_ds_returncode* status,
                            const char** value, size_t* value_sz);
\end{ccode}
Copy the string \code{str}/\code{str\_sz} into memory allocated via
\code{arena} and return the copy via \code{value} and \code{value\_sz}.  This
function will fail and return -1 if there is insufficient memory available for
copying the string.  All pointers returned by this function remain valid until
\code{arena} is destroyed.  The client should not attempt to free the returned
copy.

\paragraph{serialize int}

\begin{ccode}
void hyperdex_ds_pack_int(int64_t num, char* value);
\end{ccode}
Packs \code{num} into the bytes pointed to by \code{buf}.  This
function always succeeds.  It is the caller's responsibility to ensure that
\code{buf} points to at least \unit{8}{\byte}.

\begin{ccode}
int hyperdex_ds_copy_int(struct hyperdex_ds_arena* arena, int64_t num,
                         enum hyperdex_ds_returncode* status,
                         const char** value, size_t* value_sz);
\end{ccode}
Encode \code{num} into memory allocated via \code{arena} and return
the value via \code{value} and \code{value\_sz}.  This function will fail and
return -1 if there is insufficient memory available for encoding the number.
All pointers returned by this function remain valid until \code{arena} is
destroyed.  The client should not attempt to free the returned copy.

\paragraph{serialize float}

\begin{ccode}
void hyperdex_ds_pack_float(double num, char* value);
\end{ccode}
Pack \code{num} into the bytes pointed to by \code{buf}.  This
function always succeeds.  It is the caller's responsibility to ensure that
\code{buf} points to at least \unit{8}{\byte}.

\begin{ccode}
int hyperdex_ds_copy_float(struct hyperdex_ds_arena* arena, double num,
                           enum hyperdex_ds_returncode* status,
                           const char** value, size_t* value_sz);
\end{ccode}
Encode \code{num} into memory allocated via \code{arena} and return
the value via \code{value} and \code{value\_sz}.  This function will fail and
return -1 if there is insufficient memory available for encoding the nubmer.
All pointers returned by this function remain valid until \code{arena} is
destroyed.  The client should not attempt to free the returned copy.

\paragraph{serialize lists}

The below functions incrementally build lists, performing all relevant error
checking to ensure that the resuling HyperDex list is well-formed.  The first
element appended to the list implicitly determines the type of the list.  All
subsequent calls that push elements of a different type will fail.

\begin{ccode}
struct hyperdex_ds_list* hyperdex_ds_allocate_list(struct hyperdex_ds_arena* arena);
\end{ccode}
Create a new dynamic list.  This function will fail and return
\code{NULL} should memory allocation fail.

\begin{ccode}
int hyperdex_ds_list_insert_string(struct hyperdex_ds_list* list,
                                   const char* str, size_t str_sz,
                                   enum hyperdex_ds_returncode* status);
\end{ccode}
Append the string \code{str}/\code{str\_sz} to \code{list}.  This
function will fail and return -1 if memory allocation fails, or the list is not
a list of strings.

\begin{ccode}
int hyperdex_ds_list_insert_int(struct hyperdex_ds_list* list, int64_t num,
                                enum hyperdex_ds_returncode* status);
\end{ccode}
Append the integer \code{num} to \code{list}.  This function will fail
and return -1 if memory allocation fails, or the list is not a list of integers.

\begin{ccode}
int hyperdex_ds_list_insert_float(struct hyperdex_ds_list* list, double num,
                                  enum hyperdex_ds_returncode* status);
\end{ccode}
Append the float \code{num} to \code{list}.  This function will fail
and return -1 if memory allocation fails or the list is not a list of floats.

\begin{ccode}
int hyperdex_ds_list_finalize(struct hyperdex_ds_list*,
                              enum hyperdex_ds_returncode* status,
                              const char** value, size_t* value_sz,
                              enum hyperdatatype* datatype);
\end{ccode}
Finalize the list by writing its elements into a bytestring.  This
function returns the bytestring and the list type.  It will fail and return -1
if memory allocation fails.

\paragraph{serialize sets}

The below functions incrementally build sets, performing all relevant error
checking to ensure that the resuling HyperDex set is well-formed.  The first
element inserted into the set implicitly determines the type of the set.  All
subsequent calls that insert elements of different types will fail.

\begin{ccode}
struct hyperdex_ds_set* hyperdex_ds_allocate_set(struct hyperdex_ds_arena* arena);
\end{ccode}
Create a new dynamic set.  This function will fail and return
\code{NULL} should memory allocation fail.

\begin{ccode}
int hyperdex_ds_set_insert_string(struct hyperdex_ds_set* set,
                                  const char* str, size_t str_sz,
                                  enum hyperdex_ds_returncode* status);
\end{ccode}
Insert the string \code{str}/\code{str\_sz} into \code{set}.  This
function will fail and return -1 if memory allocation fails, or the set is not a
set of strings.

\begin{ccode}
int hyperdex_ds_set_insert_int(struct hyperdex_ds_set* set, int64_t num,
                               enum hyperdex_ds_returncode* status);
\end{ccode}
Insert the integer \code{num} into \code{set}.  This function will
fail and return -1 if memory allocation fails, or the set is not a set of
integers.

\begin{ccode}
int hyperdex_ds_set_insert_float(struct hyperdex_ds_set* set, double num,
                                 enum hyperdex_ds_returncode* status);
\end{ccode}
Insert the float \code{num} into \code{set}.  This function will fail
and return -1 if memory allocation fails, or the set is not a set of floats.

\begin{ccode}
int hyperdex_ds_set_finalize(struct hyperdex_ds_set*,
                             enum hyperdex_ds_returncode* status,
                             const char** value, size_t* value_sz,
                             enum hyperdatatype* datatype);
\end{ccode}
Finalize the set by writing its elements into a bytestring.  This
function returns the bytestring and the set type.  It will fail and return -1 if
memory allocation fails.

\paragraph{serialize maps}

The below functions incrementally build maps, performing all relevant error
checking to ensure that the resuling HyperDex map is well-formed.  The first
key/value-pair inserted into the map implicitly determines the type of the map.  All
subsequent calls that insert elements of different types will fail.

The map is built by alternating calls to the key/value functions described
below, starting with a key-based function.  This keeps the number of cases
linear in the number of primitive types a map may contain, rather than appending
key-value pairs directly (which would require a quadratic number of calls).

\begin{ccode}
struct hyperdex_ds_map* hyperdex_ds_allocate_map(struct hyperdex_ds_arena* arena);
\end{ccode}
Create a new dynamic map.  This function will fail and return
\code{NULL} should memory allocation fail.

\begin{ccode}
int hyperdex_ds_map_insert_key_string(struct hyperdex_ds_map* map,
                                      const char* str, size_t str_sz,
                                      enum hyperdex_ds_returncode* status);
\end{ccode}
Set the key of the next pair to be inserted into \code{map} to the
string specified by \code{str} and \code{str\_sz}.  This function will fail and
return -1 if memory allocation fails, or the map does not use strings for keys.

\begin{ccode}
int hyperdex_ds_map_insert_val_string(struct hyperdex_ds_map* map,
                                      const char* str, size_t str_sz,
                                      enum hyperdex_ds_returncode* status);
\end{ccode}
Set the value of the next pair to be inserted into \code{map} to the
string specified by \code{str} and \code{str\_sz}, and insert the pair.  This
function will fail and return -1 if memory allocation fails, or the map does not
use strings for values.

\begin{ccode}
int hyperdex_ds_map_insert_key_int(struct hyperdex_ds_map* map,
                                   int64_t num,
                                   enum hyperdex_ds_returncode* status);
\end{ccode}
Set the key of the next pair to be inserted into \code{map} to the
integer specified by \code{num}.  This function will fail and return -1 if
memory allocation fails, or the map does not use integers for keys.

\begin{ccode}
int hyperdex_ds_map_insert_val_int(struct hyperdex_ds_map* map,
                                   int64_t num,
                                   enum hyperdex_ds_returncode* status);
\end{ccode}
Set the value of the next pair to be inserted into \code{map} to the
integer specified by \code{num}, and insert the pair.  This function will fail
and return -1 if memory allocation fails, or the map does not use integers for
values.

\begin{ccode}
int hyperdex_ds_map_insert_key_float(struct hyperdex_ds_map* map,
                                     double num,
                                     enum hyperdex_ds_returncode* status);
\end{ccode}
Set the key of the next pair to be inserted into \code{map} to the
float specified by \code{num}.  This function will fail and return -1 if memory
allocation fails, or the map does not use floats for keys.

\begin{ccode}
int hyperdex_ds_map_insert_val_float(struct hyperdex_ds_map* map,
                                     double num,
                                     enum hyperdex_ds_returncode* status);
\end{ccode}
Set the value of the next pair to be inserted into \code{map} to the
float specified by \code{num}.  This function will fail and return -1 if memory
allocation fails, or the map does not use floats for values.

\begin{ccode}
int hyperdex_ds_map_finalize(struct hyperdex_ds_map*,
                             enum hyperdex_ds_returncode* status,
                             const char** value, size_t* value_sz,
                             enum hyperdatatype* datatype);
\end{ccode}
Finalize the map by writing its key/value-pairs  into a bytestring.
This function returns the bytestring and the map type.  It will fail and return
-1 if memory allocation fails, or an uneven number of key/value calls were made.

\subsection{Deserialization API}
\label{sec:api:c:client:deserialize}

The deserialization API provides routines to unpack ints and floats, and iterate
the elements in lists, sets, and maps.  Iterators return elements one-by-one
with a minimal amount of copying and allocation.  All iterators are used in the
same pattern.  For example, to iterate a list of integers:

\inputminted{c}{\topdir/c/client/iterate.c}

Compile and run this example with:

\begin{consolecode}
$ cc -o iterate iterate.c `pkg-config --cflags --libs hyperdex-client`
$ ./iterate
1
-1
3735928559
\end{consolecode}

The function \code{hyperdex\_ds\_iterator\_init} sets up the iterator.  Each
container data type has a specialized iteration function.  All iterators share
the same initialization function.

\begin{ccode}
void hyperdex_ds_iterator_init(struct hyperdex_ds_iterator* iter,
                               enum hyperdatatype datatype,
                               const char* value,
                               size_t value_sz);
\end{ccode}
Initialize an iterator for the given data type/value.  This function
always succeeds.

\paragraph{deserialize string}

No deserialization is necessary for string data types.

\paragraph{deserialize int}

\begin{ccode}
int hyperdex_ds_unpack_int(const char* buf, size_t buf_sz, int64_t* num);
\end{ccode}
Unpack \code{num} from \code{buf}/\code{buf\_sz}.  This function will
fail and return -1 if \code{buf\_sz} is not exactly \unit{8}{\byte}.

\paragraph{deserialize float}

\begin{ccode}
int hyperdex_ds_unpack_float(const char* buf, size_t buf_sz, double* num);
\end{ccode}
Unpack \code{num} from \code{buf}/\code{buf\_sz}.  This function will
fail and return -1 if \code{buf\_sz} is not exactly \unit{8}{\byte}.

\paragraph{deserialize lists}

\begin{ccode}
int hyperdex_ds_iterate_list_string_next(struct hyperdex_ds_iterator* iter,
                                         const char** str, size_t* str_sz);
\end{ccode}
Return the next string element in the list.  This function will return
1 if an element is returned, 0 if there are no elements to return, and -1 if the
list of strings is malformed.  The value stored in \code{*str} is a pointer into
the list of strings and should not be free'd by the application.

\begin{ccode}
int hyperdex_ds_iterate_list_int_next(struct hyperdex_ds_iterator* iter, int64_t* num);
\end{ccode}
Return the next integer element in the list.  This function will
return 1 if an element is returned, 0 if there are no elements to return, and -1
if the list of integers is malformed.

\begin{ccode}
int hyperdex_ds_iterate_list_float_next(struct hyperdex_ds_iterator* iter, double* num);
\end{ccode}
Return the next float element in the list.  This function will return
1 if an element is returned, 0 if there are no elements to return, and -1 if the
list of floats is malformed.

\paragraph{deserialize sets}

\begin{ccode}
int hyperdex_ds_iterate_set_string_next(struct hyperdex_ds_iterator* iter,
                                        const char** str, size_t* str_sz);
\end{ccode}
Return the next string element in the set.  This function will return
1 if an element is returned, 0 if there are no elements to return, and -1 if the
set of strings is malformed.  The value stored in \code{*str} is a pointer into
the set of strings and should not be free'd by the application.

\begin{ccode}
int hyperdex_ds_iterate_set_int_next(struct hyperdex_ds_iterator* iter, int64_t* num);
\end{ccode}
Return the next integer element in the set.  This function will return
1 if an element is returned, 0 if there are no elements to return, and -1 if the
set of ints is malformed.

\begin{ccode}
int hyperdex_ds_iterate_set_float_next(struct hyperdex_ds_iterator* iter, double* num);
\end{ccode}
Return the next float element in the set.  This function will return 1
if an element is returned, 0 if there are no elements to return, and -1 if the
set of ints is malformed.

\paragraph{deserialize maps}

\begin{ccode}
int hyperdex_ds_iterate_map_string_string_next(struct hyperdex_ds_iterator* iter,
                                               const char** key, size_t* key_sz,
                                               const char** val, size_t* val_sz);
\end{ccode}
Return the next pair of (string, string) in the map.  This function
will return 1 if an element is returned, 0 if there are no elements to return,
and -1 if the map is malformed.  The values stored in \code{*key} and
\code{*val} are pointers into the map and should not be free'd by the
application.

\begin{ccode}
int hyperdex_ds_iterate_map_string_int_next(struct hyperdex_ds_iterator* iter,
                                            const char** key, size_t* key_sz,
                                            int64_t* val);
\end{ccode}
Return the next pair of (string, int) in the map.  This function will
return 1 if an element is returned, 0 if there are no elements to return, and -1
if the map is malformed.  The value stored in \code{*key} is a pointer into the
map and should not be free'd by the application.

\begin{ccode}
int hyperdex_ds_iterate_map_string_float_next(struct hyperdex_ds_iterator* iter,
                                              const char** key, size_t* key_sz,
                                              double* val);
\end{ccode}
Return the next pair of (string, float) in the map.  This function
will return 1 if an element is returned, 0 if there are no elements to return,
and -1 if the map is malformed.  The value stored in \code{*key} is a pointer
into the map and should not be free'd by the application.

\begin{ccode}
int hyperdex_ds_iterate_map_int_string_next(struct hyperdex_ds_iterator* iter,
                                            int64_t* key,
                                            const char** val, size_t* val_sz);
\end{ccode}
Return the next pair of (int, string) in the map.  This function will
return 1 if an element is returned, 0 if there are no elements to return, and -1
if the map is malformed.  The value stored in \code{*val} is a pointer into the
map and should not be free'd by the application.

\begin{ccode}
int hyperdex_ds_iterate_map_int_int_next(struct hyperdex_ds_iterator* iter,
                                         int64_t* key, int64_t* val);
\end{ccode}
Return the next pair of (int, int) in the map.  This function will
return 1 if an element is returned, 0 if there are no elements to return, and -1
if the map is malformed.

\begin{ccode}
int hyperdex_ds_iterate_map_int_float_next(struct hyperdex_ds_iterator* iter,
                                           int64_t* key, double* val);
\end{ccode}
Return the next pair of (int, float) in the map.  This function will
return 1 if an element is returned, 0 if there are no elements to return, and -1
if the map is malformed.

\begin{ccode}
int hyperdex_ds_iterate_map_float_string_next(struct hyperdex_ds_iterator* iter,
                                              double* key,
                                              const char** val, size_t* val_sz);
\end{ccode}
Return the next pair of (float, string) in the map.  This function
will return 1 if an element is returned, 0 if there are no elements to return,
and -1 if the map is malformed.  The value stored in \code{*val} is a pointer
into the map and should not be free'd by the application.

\begin{ccode}
int hyperdex_ds_iterate_map_float_int_next(struct hyperdex_ds_iterator* iter,
                                           double* key, int64_t* val);
\end{ccode}
Return the next pair of (float, int) in the map.  This function will
return 1 if an element is returned, 0 if there are no elements to return, and -1
if the map is malformed.

\begin{ccode}
int hyperdex_ds_iterate_map_float_float_next(struct hyperdex_ds_iterator* iter,
                                             double* key, double* val);
\end{ccode}
Return the next pair of (float, float) in the map.  This function will
return 1 if an element is returned, 0 if there are no elements to return, and -1
if the map is malformed.

\subsection{Memory Management Utilties}
\label{sec:api:c:client:memory}

The data structures API provides utility functions for allocating structures
from the arena, obviating the need to free them individually.

\begin{ccode}
struct hyperdex_client_attribute*
hyperdex_ds_allocate_attribute(struct hyperdex_ds_arena* arena, size_t sz);
\end{ccode}
Allocate an array of \code{struct hyperdex\_client\_attribute}.  On
success, this function returns a non-null pointer containing \code{sz} elements.
On failure, the function returns \code{NULL}, indicating that memory allocation
failed.  The memory will remain valid until the arena is destroyed and should
not be free'd independently by the application.

\begin{ccode}
struct hyperdex_client_attribute_check*
hyperdex_ds_allocate_attribute_check(struct hyperdex_ds_arena* arena, size_t sz);
\end{ccode}
Allocate an array of \code{struct hyperdex\_client\_attribute\_check}.
On success, this function returns a non-null pointer containing \code{sz}
elements.  On failure, the function returns \code{NULL}, indicating that memory
allocation failed.  The memory will remain valid until the arena is destroyed
and should not be free'd independently by the application.

\begin{ccode}
struct hyperdex_client_map_attribute*
hyperdex_ds_allocate_map_attribute(struct hyperdex_ds_arena* arena, size_t sz);
\end{ccode}
Allocate an array of \code{struct hyperdex\_client\_map\_attribute}.
On success, this function returns a non-null pointer containing \code{sz}
elements.  On failure, the function returns \code{NULL}, indicating that memory
allocation failed.  The memory will remain valid until the arena is destroyed
and should not be free'd independently by the application.

\section{Attributes}
\label{sec:api:c:client:attributes}

In HyperDex, {\em attributes} specify named values that comprise an object.
For instance, in Chapter~\nameref{chap:quick-start}, the phonebook space has
attributes ``username'', ``first'', ``last'', and ``phone''.  The C API
represents such attributes using \code{struct hyperdex\_client\_attribute}.  The
C definition of this struct is:

\begin{ccode}
struct hyperdex_client_attribute
{
    const char* attr; /* NULL-terminated */
    const char* value;
    size_t value_sz;
    enum hyperdatatype datatype;
};
\end{ccode}

This struct specifies the name, value, and data type of the attribute.  The
\code{attr} field is a NULL-terminated C-string that names the attribute
affected by the value.  The \code{value} and \code{value\_sz} fields contain a
properly formatted byte string and its size.  The \code{datatype} field
indicates the encoding of the byte string.

The interpretation of an attribute is dependent upon the operation being
performed.  In the case of \code{hyperdex\_client\_put}, the attributes directly
convey the values to be stored; \code{hyperdex\_client\_get} returns the
stored attributes.  Other operations, such as
\code{hyperdex\_client\_string\_prepend} interpret the attribute as an argument
to the operation.  In the case of prepend, the attribute specifies the value to
be prepended.

\section{Map Attributes}
\label{sec:api:c:client:map-attributes}

Some HyperDex operations affect key-value pairs contained within maps.  These
operations use \code{struct hyperdex\_client\_map\_attribute} to specify the
name of the attribute affected, the key within the map, and the value associated
with that key.  The C definition of this struct is:

\begin{ccode}
struct hyperdex_client_map_attribute
{
    const char* attr; /* NULL-terminated */
    const char* map_key;
    size_t map_key_sz;
    enum hyperdatatype map_key_datatype;
    const char* value;
    size_t value_sz;
    enum hyperdatatype value_datatype;
};
\end{ccode}

The struct specifies the name, key, and value of to be used for the operation.
The \code{map\_key\_*} and \code{value\_*} fields specify, respectively, the key
and value of the element within the map.  The relative fields are specified as
byte strings with associated data types.  The \code{attr} field specifies the
name of the map.

\section{Predicates}
\label{sec:api:c:client:predicates}

In HyperDex, a {\em predicate} is an expression about an attribute that is true
or false.  Predicates are specified to HyperDex using \code{struct
hyperdex\_client\_attribute\_check} which is defined as:

\begin{ccode}
struct hyperdex_client_attribute_check
{
    const char* attr; /* NULL-terminated */
    const char* value;
    size_t value_sz;
    enum hyperdatatype datatype;
    enum hyperpredicate predicate;
};
\end{ccode}

Note that this struct closely resembles \code{struct
hyperdex\_client\_attribute}, with the addition of a field named
\code{predicate}.  This field is an enum with the following values:

\begin{description}
\item[\code{HYPERPREDICATE\_FAIL}] Always fail.
\item[\code{HYPERPREDICATE\_EQUALS}] Check that the existing value is equal to
    the one specified by \code{value}/\code{value\_sz}.
\item[\code{HYPERPREDICATE\_LESS\_EQUAL}] Check that the existing value is less
    than or equal to the one specified by \code{value}/\code{value\_sz}.
\item[\code{HYPERPREDICATE\_GREATER\_EQUAL}] Check that the existing value is
    greater than or equal to the one specified by \code{value}/\code{value\_sz}.
\item[\code{HYPERPREDICATE\_REGEX}] Check that the existing value matches the
    regular expression stored in as a string in \code{value}/\code{value\_sz}.
\item[\code{HYPERPREDICATE\_LENGTH\_EQUALS}] Check that the existing container
    or string has a length equal to the integer stored in
    \code{value}/\code{value\_sz}.
\item[\code{HYPERPREDICATE\_LENGTH\_LESS\_EQUAL}] Check that the existing
    container or string has a length less than or equal to the integer stored in
    \code{value}/\code{value\_sz}.
\item[\code{HYPERPREDICATE\_LENGTH\_GREATER\_EQUAL}] Check that the existing
    container or string has a length greater than or equal to the integer stored
    in \code{value}/\code{value\_sz}.
\item[\code{HYPERPREDICATE\_CONTAINS}] Check that the container contains an
    element matching \code{value}/\code{value\_sz}.
\end{description}

\section{Error Handling}
\label{sec:api:c:client:error-handling}

Every call in the client provides a means for reporting failure.  After each
call, your application should check for the error and react appropriately.
Depending upon the error, your application may retry the request, or may need to
take more drastic action.

Errors are typically reported via \code{enum hyperdex\_client\_returncode}
defined in \code{hyperdex/client.h}.  Values for this enum fall into three
categories:  values returned during normal operation, values returned to
indicate anticipated errors, and values that should never be returned in
practice.

The common-case returncodes are:

\begin{description}
\item[\code{HYPERDEX\_CLIENT\_SUCCESS}]  The operation was successful and no
    errors occurred.
\item[\code{HYPERDEX\_CLIENT\_NOTFOUND}]  The operation finished because the
    requested object was not found.
\item[\code{HYPERDEX\_CLIENT\_SEARCHDONE}]  An operation that potentially
    returns multiple objects.  For instance, a search has
    finished and will no longer be returned via loop.
\item[\code{HYPERDEX\_CLIENT\_CMPFAIL}]  The predicate specified as part of a
    conditional operation was not true.
\item[\code{HYPERDEX\_CLIENT\_READONLY}] The cluster is in read-only mode and
    not accepting write operations.
\end{description}

The following errors stem from environmental problems or problems with the
application's usage of HyperDex.  These errors are generally easy to remedy and
are anticipated by the client library.

\begin{description}
\item[\code{HYPERDEX\_CLIENT\_UNKNOWNSPACE}] The specified space does not exist.
    Ensure that a space exists before trying to manipulate its data.
\item[\code{HYPERDEX\_CLIENT\_COORDFAIL}] The connection to the coordinator has
    failed.  The application should back off before retrying.

    Note that the connection to the coordinator is not a simple TCP connection,
    and is redundant if the coordinator consists of multiple servers.  This
    error indicates to the application that the redundancy has failed and that
    the application should back-off.
\item[\code{HYPERDEX\_CLIENT\_SERVERERROR}] A server returned a nonsensical
    result to the client library.  Generally retrying the request should be
    sufficient to overcome the problem.
\item[\code{HYPERDEX\_CLIENT\_POLLFAILED}] The poll system call failed in an
    unexpected manner.  Typically, this means that the application using the
    HyperDex library has mismanaged its file descriptors and improperly altered
    descriptors in use by the HyperDex library.

    This generally indicates that there is a bug in the HyperDex client library,
    the application using the library, or both.
\item[\code{HYPERDEX\_CLIENT\_OVERFLOW}] An integer operation failed to complete
    because it would have resulted in signed overflow.
\item[\code{HYPERDEX\_CLIENT\_RECONFIGURE}] The server responsible for managing
    the operation failed while the operation was in-flight.
\item[\code{HYPERDEX\_CLIENT\_TIMEOUT}] The \code{hyperdex\_client\_loop}
    operation exceeded its timeout without completing an outstanding operation.
    This does not affect the status of any outstanding operation.
\item[\code{HYPERDEX\_CLIENT\_UNKNOWNATTR}] The operation references an
    attribute that is not part of the space.  Make sure to use attributes within
    the space's schema.
\item[\code{HYPERDEX\_CLIENT\_DUPEATTR}] The operation references an attribute
    multiple times in a way that is not permitted.
\item[\code{HYPERDEX\_CLIENT\_NONEPENDING}] The \code{hyperdex\_client\_loop}
    call was made, but no operations were outstanding.  This generally indicates
    that the loop call was called too many times for the operations issued.
\item[\code{HYPERDEX\_CLIENT\_DONTUSEKEY}] The operation attempted to mutate the
    key, which is not permitted.
\item[\code{HYPERDEX\_CLIENT\_WRONGTYPE}] The attribute or predicate was not
    compatible with the type in the space's schema.  For example, the operation
    may have attempted to issue a PUT operation that writes an integer to a
    non-integer datatype, or tries to perform string operations on a non-string
    data type.  Check that the operation performed is compatible with the data
    type of the affected attributes.
\item[\code{HYPERDEX\_CLIENT\_NOMEM}] The library failed to allocate memory.
\item[\code{HYPERDEX\_CLIENT\_INTERRUPTED}] The HyperDex library was interrupted
    by a signal.  Read more about signals in Section~\ref{sec:api:c:client:signals}.
\item[\code{HYPERDEX\_CLIENT\_CLUSTER\_JUMP}] The client library started
    receiving configurations for a different HyperDex cluster.  This can happen
    if a new coordinator is restarted on the same address that the client
    connects to.  This error will not be persistent.  The client library will
    switch to the new cluster configuration, and this error just serves as a
    notification to the application.
\item[\code{HYPERDEX\_CLIENT\_OFFLINE}] All servers responsible for handling the
    specified operation are currently offline and unavailable, whether due to
    failure or planned downtime.
\end{description}

The following errors indicate significant bugs within the client or application.
In practice they should never happen and indicate bugs within HyperDex itself.
They are used as one would use an \code{assert} statement to enforce an
invariant.

\begin{description}
\item[\code{HYPERDEX\_CLIENT\_INTERNAL}] One or more of the HyperDex client
    library's internal invariants have been broken.  It's best to destroy and
    recreate the client.
\item[\code{HYPERDEX\_CLIENT\_EXCEPTION}] The C library is implemented
    internally using C++.  C++ generated an unhandled exception that was caught
    at the C boundary.  This indicates a bug in HyperDex, and exists only as a
    safeguard.  Applications should never see this error.
\item[\code{HYPERDEX\_CLIENT\_GARBAGE}] This value is reserved as a well-defined
    value that the library will never return, and that is not used as a constant
    anywhere else within HyperDex.
\end{description}

Note that an asynchronous application should distinguish between {\em local}
errors which affect one outstanding operation, and {\em global} errors that
transiently affect all operations, but do not change the completion status of
those operations.

Local errors are always returned via the \code{enum
hyperdex\_client\_returncode*} pointer passed at the time the application
initiated the operation.  These errors may either result from the application
returning a negative operation id, in which case the operation immediately
completes, or from the result of a successful \code{hyperdex\_client\_loop}
call.  In either case, the error has no impact on the result of any other
operation.

Global errors are always returned via the \code{enum
hyperdex\_client\_returncode*} pointer passed to the most recent invocation of
\code{hyperdex\_client\_loop}.  These errors are not localized to any particular
operation and indicate errors that are non-permanent.  Example global errors
include application-requested timeouts, interruptions by signals, and temporary
non-connectivity with the coordinator.

\section{Operations}
\label{sec:api:c:client:ops}

\input{\topdir/c/client/ops}
\pagebreak

\section{Working with Signals}
\label{sec:api:c:client:signals}

The HyperDex client library is signal-safe.  Should a signal interrupt the
client during a blocking operation, it will return
\code{HYPERDEX\_CLIENT\_INTERRUPTED}.

\section{Working with Threads}
\label{sec:api:c:client:threads}

The HyperDex client library is fully reentrant.  Instances of \code{struct
hyperdex\_client} and their associated state may be accessed from multiple
threads, provided that the application employes its own synchronization that
provides mutual exclusion.

Put simply, a multi-threaded application should protect each \code{struct
hyperdex\_client} instance with a mutex or lock to ensure correct operation.
